#ifndef __C__SOCKETS_H__
#define __C__SOCKETS_H__
#include<start.h>
#include<vector>
#include<unordered_map>
#include<list>
#include <sys/epoll.h> //epoll
#include <sys/socket.h>
#include <atomic>   //c++11里的原子操作
#include <pthread.h>
#include <sys/time.h>  //gettimeofday
#include <time.h>      //localtime_r
#define NGX_LISTEN_BACKLOG  511   //已完成连接队列，nginx给511，我们也先按照这个来：不懂这个数字的同学参考第五章第四节
#define NGX_MAX_EVENTS      (NGX_LISTEN_BACKLOG+1)

#define PACKET_HEAD_LEN  8 //  包头结构体的内存固定长度
#define PACKET_MSG_MAX_LEN  50000 //信息的字节数
#define PACKET_MAX_LEN  (PACKET_HEAD_LEN+PACKET_MSG_MAX_LEN)

//通信 收包状态定义
#define _PKG_HD_INIT         0  //初始状态，准备接收数据包头
#define _PKG_HD_RECVING      1  //接收包头中，包头不完整，继续接收中
#define _PKG_BD_INIT         2  //包头刚好收完，准备接收包体
#define _PKG_BD_RECVING      3  //接收包体中，包体不完整，继续接收中，处理后直接回到_PKG_HD_INIT状态

//epoll action 操作字
#define _ACT_ADD_         0
#define _ACT_DEL_         1
#define _ACT_SET_         2

//节点的空闲等级
#define _LS_BUSY_         0
#define _LS_NORM_         1
#define _LS_FREE_         2

//节点类型
#define _LISTEN_ 0
#define _TRANS_ 1


/*声明方法   为了结构与类之间的相互引用    以及结构中定义回调函数指针  这里需要一些技巧*/
typedef  struct ser_listen S_ser_listen, * LP_ser_listen; //提前声明结构体
typedef  struct connection_node S_connection_node, * LP_connection_node;
typedef class CSockets CSockets;
typedef void (CSockets::* event_handler_pt)(LP_connection_node c) ; //声明成员函数指针  参数为一个连接信息  
																											//这里声明是为了在结构体里面定义回调

#define _CMD_START	                    0  
#define _CMD_REGISTER 		            _CMD_START + 5   //注册
#define _CMD_LOGIN 		                _CMD_START + 6   //登录
#pragma pack (1) //对齐方式,1字节对齐【结构之间成员不做任何字节对齐：紧密的排列在一起】
//一些和网络通讯相关的结构
//包头结构
typedef struct //包头 
{
	uint16_t PkgLen;  
	uint16_t PkgMsgCmd;    
	int            crc32;    
}S_PKG_HEADER, * LP_PKG_HEADER;


typedef struct _STRUC_MSG_HEADER
{
	LP_connection_node pConn;    
}STRUC_MSG_HEADER, * LPSTRUC_MSG_HEADER;

#pragma pack() //取消指定对齐，恢复缺省对齐
struct ser_listen
{
	int skt;
	int port;
	LP_connection_node conn_e;
};

struct connection_node //连接池节点
{
	int skt = -1;// 响应套接字

	char node_type;
	//char node_ls_lvl = _LS_FREE_;
	LP_connection_node clr_fr;
	LP_connection_node clr_bk;
	//LP_node_msg nodeinfo;

	LP_ser_listen s_listen;//绑定listen结构
	LP_connection_node nx = nullptr; //连接指针

	uint32_t events; //用来保存注册的  需注意事件
	unsigned                  instance : 1;     
    struct sockaddr           s_sockaddr;     //
	uint8_t                      w_ready;        //

	event_handler_pt      rhandler;       //读事件的相关处理方法 
	event_handler_pt      whandler;       //写事件的相关处理方法 
	char packet_head_rec[PACKET_HEAD_LEN];

	
	//LP_rec_packet_msg  rec_msg;      //记录在该连接上收到的数据  结构
	uint8_t                  iCurrsequence;  //引入的一个序号，每次分配出去时+1，此法也有可能在一定程度上检测错包废包，具体怎么用，用到了再说

	pthread_mutex_t _conn_mtx_recv;
	pthread_mutex_t _conn_mtx_deal;
	pthread_mutex_t _conn_mtx_send;


	//管理相关
	time_t time_rw;
	int business_data_use;


	//发包相关
	char* pBuffSend_head;
	char* pBuffSend_pos;
	unsigned short rs_len;

	short state_flag = 0;

	char* p_rec_now; //用来指向当前读取位置 （即数据采集临时存储的位置）
	uint16_t  rec_len; // 与p_rec_now调用recv 一次的长度

	bool new_data_flag;

	char* p_new_data_mem;  


	//web相关

	char packet_head_rec_web[800];
	char* p_rec_now_web;
	uint16_t  rec_len_web; // 与p_rec_now调用recv 一次的长度
	char* p_new_data_mem_web;  
	
};
class CSockets
{
public:
	CSockets();
	virtual ~CSockets();

	virtual bool initialize();
	virtual void threadRecvProcFunc(char* pMsgBuf);                    
	virtual int threadSendProcFunc(char* pMsgBuf);                   
	void recv_thread_init(int);
	int _request_input_deal(LP_connection_node trans_node);
	int _request_input_deal_web(LP_connection_node);
	int  ngx_epoll_init();                                             
	//epoll功能初始化
	//包装了epoll_create(m_worker_connections);
																	   //m_worker_connections一般表示连接数

	int  ngx_epoll_set_event(int fd,
		int                action,
		bool              et_flag,
		uint32_t eventtype,
		uint32_t           flag,             //标志，具体含义取决于eventtype  当eventtype是修改字  则flag和action组合代表修改动作
		LP_connection_node c);

	virtual int  ngx_epoll_process_events(int timer);                          //epoll等待接收和处理事件
	void ngx_close_connection(LP_connection_node c);
		//关闭连接池节点  （包装释放节点）
	bool ReadConf();
	static void* _request_input(void* threadData);

	void event_accept(LP_connection_node oldc);//建立新连接时  监听套接字绑定的处理函数
	void ngx_wait_request_handler(LP_connection_node trans_node);//数据传输套接字绑定的处理函数

	size_t ngx_sock_ntop(struct sockaddr* sa, int port, u_char* text, size_t len);
	ssize_t recv_et(LP_connection_node c, char* buff, ssize_t buflen);
	ssize_t send_atm(LP_connection_node c, char* buff, ssize_t size);

	bool recv_msg_pro_head(LP_connection_node );
	void recv_msg_pro_body(LP_connection_node );
	void inMsgRecvQueue(char* buf); 
	void tmpoutMsgRecvQueue();

	void exchange_epoll_in_out(LP_connection_node, bool);
	int get_m_conn_n_max()
	{
		return (int)m_conn_n_max;
	}
	int get_m_free_conn_n()
	{
		return (int)m_free_conn_n;
	}

private:
	bool listen_sockets_close();
	bool listen_sockets_open();
	LP_connection_node  conn_node_creat(int skt, char);
	void conn_node_free(LP_connection_node c);


private:
	struct ThreadItem
	{
		int id;
		CSockets* ck;
		LP_connection_node conn;
		pthread_t _handler;
		unsigned char stat;
		ThreadItem(CSockets* c) :ck(c), stat(0) {}
		~ThreadItem() {}
	};
private:
	int ListenPortNum;
	std::vector<LP_ser_listen> sockets_list;

	int                            m_worker_connections;             
	int                            m_epollhandle;                    

	LP_connection_node             m_p_conn_head;                 
	LP_connection_node             m_pfree_conn_head;                
	LP_connection_node             m_pfree_conn_tail;                 

	std::atomic<int>                              m_conn_n_max;                   
	std::atomic<int>                              m_free_conn_n;                  

	struct epoll_event             m_events[NGX_MAX_EVENTS];         
public:
	pthread_mutex_t pth_mtx_conn_create;
	pthread_mutex_t pth_mtx_conn_free;

	std::vector<ThreadItem*>thread_vec;       

	static pthread_mutex_t pth_mtx_conn_recv;
	static pthread_mutex_t pth_mtx_conn_recv_init;
	static pthread_cond_t pth_cond_conn_recv;
	static pthread_mutex_t pth_mtx_conn_recv_push;
	std::list<LP_connection_node>  Recv_nodes;        
	std::atomic<int>                        recv_nodes_sz;
	
	static pthread_mutex_t pth_mtx_conn_send;
	static pthread_mutex_t pth_mtx_conn_send_push;
	static pthread_cond_t pth_cond_conn_send;

	std::list<LP_connection_node>  Send_nodes;        
	std::atomic<int>                        send_nodes_sz;
	std::atomic<int>      packet_cnt;
	static bool close_recv_th;
	static bool close_send_th;
	
	static int cnt_run_th_recv;
	static int cnt_run_th_send;

	//std::vector<int> skts_vec;

	std::unordered_map<int, bool> fd_use_f;

};


#endif